import SceneController, { MoveInfo, SceneState, TButton, TLayout, TScene } from "./SceneControl";

export default abstract class Scene extends cc.Component {

    tscene: TScene = null;

    public abstract initScene();

    protected onEnable(): void {
        this.tscene = new TScene(this.node);
    }

    public pauseService() {
        SceneController.getInstance().inService = false;
    }

    public resumeService() {
        SceneController.getInstance().inService = true;
    }

    protected onDisable(): void {
        this.tscene.removeThis();
    }

    /**
     * 将节点按y坐标从高到低和x坐标从低到高排序，按传入的网格格子高度为一行，分组成一个二维网格数组。
     * 可以选择将结果以节点数组或 TButton 数组的形式返回。
     * @param nodes - 需要排序分组的节点数组
     * @param gridCellHeight - 网格格子的高度，默认为 150
     * @param returnAsButton - 是否以 TButton 数组的形式返回结果，默认为 false
     * @returns 排序分组后的二维数组，元素类型为 cc.Node 或 TButton
     */
    public static sortNodesIntoGrid(nodes: cc.Node[], gridCellHeight: number = 150, returnAsButton: boolean = false): cc.Node[][] | TButton[][] {
        if (nodes.length == 0) {
            return [];
        }

        // 构建节点到世界位置的映射表 
        const positionMap = new Map<cc.Node, cc.Vec3>();
        nodes.forEach((node) => {
            positionMap.set(node, node.convertToWorldSpaceAR(cc.Vec3.ZERO));
        });

        // 按照世界 y 坐标从高到低排序
        nodes.sort((a, b) => {
            const aPos = positionMap.get(a);
            const bPos = positionMap.get(b);
            if (aPos && bPos) {
                return bPos.y - aPos.y;
            }
            return 0;
        });

        const grid: cc.Node[][] = [];
        let currentRow: cc.Node[] = [];
        let currentRowY: number | null = null;

        for (const node of nodes) {
            const worldPosition = positionMap.get(node);
            if (worldPosition) {
                if (currentRowY === null) {
                    currentRowY = worldPosition.y;
                    currentRow.push(node);
                } else if (currentRowY - worldPosition.y < gridCellHeight) {
                    currentRow.push(node);
                } else {
                    // 对当前行按世界 x 坐标从低到高排序
                    currentRow.sort((a, b) => {
                        const aPos = positionMap.get(a);
                        const bPos = positionMap.get(b);
                        if (aPos && bPos) {
                            return aPos.x - bPos.x;
                        }
                        return 0;
                    });
                    grid.push(currentRow);
                    currentRow = [node];
                    currentRowY = worldPosition.y;
                }
            }
        }

        // 处理最后一行
        if (currentRow.length > 0) {
            currentRow.sort((a, b) => {
                const aPos = positionMap.get(a);
                const bPos = positionMap.get(b);
                if (aPos && bPos) {
                    return aPos.x - bPos.x;
                }
                return 0;
            });
            grid.push(currentRow);
        }
        if (returnAsButton) {
            return grid.map(row => row.map(node => new TButton(node))) as TButton[][];
        }

        return grid;
    }

    /**
     * 递归获取根节点下所有满足指定条件的子节点
     * @param root - 根节点，从该节点开始查找满足条件的子节点
     * @param condition - 用于判断节点是否符合条件的函数，默认为判断节点是否有触摸开始事件监听器
     * @returns 满足条件的子节点数组
     */
    public static getAllMatchingChildren(root: cc.Node, condition: (node: cc.Node) => boolean = Scene.isButton) {
        // 用于存储满足条件的子节点的数组
        let result: cc.Node[] = [];
        // 遍历根节点的直接子节点
        root.children.forEach(child => {
            // 检查当前子节点是否满足条件
            if (condition(child)) {
                // 若满足条件，则将其添加到结果数组中
                result.push(child);
            }
            // 递归调用该函数，获取当前子节点的所有满足条件的子节点，并将结果合并到结果数组中
            result = result.concat(this.getAllMatchingChildren(child, condition));
        });
        // 返回满足条件的子节点数组
        return result;
    }

    public static isButton(node: cc.Node): boolean {
        return node.hasEventListener(cc.Node.EventType.TOUCH_START)
            || node.hasEventListener(cc.Node.EventType.TOUCH_END)
            || node.hasEventListener(cc.Node.EventType.TOUCH_CANCEL)
    }

    static getScrollLayout(scrollView: cc.ScrollView): TLayout {
        let layout = new TLayout(scrollView.node.name);
        let content = scrollView.content;
        let btns = Scene.sortNodesIntoGrid(content.children) as TButton[][];

        let fingerIn;
        if (scrollView.horizontal) {
            fingerIn = (moveinfo: MoveInfo) => {
                scrollView.scrollToPercentHorizontal(moveinfo.targetLayout.site.line / layout.buttonList.length - 1, 0.2);
            }
        } else {
            fingerIn = (moveinfo: MoveInfo) => {
                scrollView.scrollToPercentVertical(1 - (moveinfo.targetLayout.site.row / layout.buttonList.length - 1), 0.2);
            }
        }

        btns.forEach((row) => {
            row.forEach((btn) => {
                btn.addBeforeFingerInEvent(fingerIn);
            })
        })

        layout.buttonList = btns;
        layout.site.row = layout.buttonList.length - 1;
        return layout;
    }

}
